package com.alibaba.nacos.console.service;

import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.console.model.Namespace;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.plugin.auth.api.Permission;
import com.alibaba.nacos.plugin.auth.api.Resource;
import com.alibaba.nacos.plugin.auth.exception.AccessException;
import com.alibaba.nacos.plugin.auth.impl.authenticate.IAuthenticationManager;
import com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl;
import com.alibaba.nacos.plugin.auth.impl.users.NacosUser;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author caokai50
 * @Description
 * @Date 2024/8/14 14:30
 */
@Service
public class NamespaceAuthEnhanceOperationService {

    private NamespaceOperationService namespaceOperationService;

    private NacosRoleServiceImpl roleService;

    IAuthenticationManager authenticationManager;

    public NamespaceAuthEnhanceOperationService(NamespaceOperationService namespaceOperationService,
                                                NacosRoleServiceImpl roleService,
                                                IAuthenticationManager authenticationManager) {
        this.namespaceOperationService = namespaceOperationService;
        this.roleService = roleService;
        this.authenticationManager = authenticationManager;
    }

    public List<Namespace> getNamespaceList(HttpServletRequest req) {
        List<Namespace> namespaceList = namespaceOperationService.getNamespaceList();
        try {
            String accessToken = req.getParameter(Constants.ACCESS_TOKEN);
            NacosUser nacosUser = authenticationManager.authenticate(accessToken);
            List<Namespace> permissionNamespaceList = new ArrayList<>();
            for (Namespace namespace : namespaceList) {
                Permission permission = getPermission(namespace);
                boolean hasPermission = roleService.hasPermission(nacosUser, permission);
                if (StringUtils.equalsIgnoreCase(Constants.DEFAULT_NAMESPACE_ID, namespace.getNamespaceShowName()) || hasPermission) {
                    permissionNamespaceList.add(namespace);
                }
            }
            namespaceList = permissionNamespaceList;
        } catch (AccessException e) {
            if (Loggers.AUTH.isDebugEnabled()) {
                Loggers.AUTH.debug("access denied, request: {} {}, reason: {}", req.getMethod(), req.getRequestURI(),
                        e.getErrMsg());
            }
        } catch (Exception e) {
            if (Loggers.AUTH.isDebugEnabled()) {
                Loggers.AUTH.debug("according to the permission filter error, request: {} {}, reason: {}", req.getMethod(), req.getRequestURI(), ExceptionUtils.getFullStackTrace(e));
            }
        }
        // public开头的命名空间排前面
        sortNamespaceList(namespaceList);
        return namespaceList;
    }

    /**
     * 获取权限类
     *
     * @param namespace
     * @return
     */
    public static Permission getPermission(Namespace namespace) {

        String namespaceId = namespace.getNamespace();
        String group = "";
        String name = "";
        Properties properties = new Properties();
        String action = "r";
        properties.putIfAbsent(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.ACTION, action);
        Resource resource = new Resource(namespaceId, group, name, "config", properties);
        Permission permission = new Permission(resource, action);
        return permission;
    }

    /**
     * 对 NameSpace 列表排序
     *
     * @param namespaceList
     */
    private static void sortNamespaceList(List<Namespace> namespaceList) {
        if (CollectionUtils.isNotEmpty(namespaceList)) {
            namespaceList.sort((namespace1, namespace2) -> {
                String n1 = namespace1.getNamespaceShowName();
                String n2 = namespace2.getNamespaceShowName();
                if (n1.equals("public")) {
                    return -1;
                } else if (n2.equals("public")) {
                    return 1;
                } else if (n1.startsWith("public") && n2.startsWith("public")) {
                    return 0;
                } else if (n1.startsWith("public")) {
                    return -1;
                } else if (n2.startsWith("public")) {
                    return 1;
                } else {
                    return 0;
                }
            });
        }
    }
}
